---
id: ipc
title: 33. IPC 进程通信
sidebar_label: 33. IPC 进程通信
---

## 33.1 什么是 `IPC`

> 引用百度百科
>
> IPC（Inter-Process Communication，进程间通信）。进程间通信是指两个进程的数据之间产生交互。

通俗点说，`IPC` 可以实现不同应用程序间通信（交互数据）。

## 33.2 实现 `IPC` 通信方式

- 半双工 Unix 管道
- FIFOs(命名管道)
- **消息队列**（常用模式）
- 信号量
- **共享内存**（常用模式，`Furion` 框架默认实现方式）
- **网络 Socket**（常用模式）

## 33.3 `IPC` 通信模式

`IPC` 本身指的是 `进程间` 通信，但 `Furion` 框架将内置 `进程间/内` 两种进程通信模式。

- `进程内通信`：`Furion` 采用 `Channel` 管道提供进程内通信
- `进程外通信`：`Furion` 采用 `MemoryMapperFile` 共享内存方式实现进程外通信（后续版本完善）

## 33.4 进程内通信（线程间）

进程内通信俗称线程间通信，`Furion` 框架采用 `C#` 提供的 `Channel（管道）` + `Lazy` + `Task.Factory` 实现长时间高性能的线程间通信机制。`Channel` 管道也是目前 `.NET/C#` 实现 `生产者-订阅者` 模式最简易且最为强大的实现。

### 33.4.1 了解 `Channel`

`Channel` 是在 `.NET Core 2.1+` 版本之后加入。`Channel` 底层实现是一个高效的、线程安全的队列，可以在线程之间传递数据。

`Channel` 的主要应用场景是 `发布/订阅、观察者模式` 中使用，如：`事件总线` 就是最好的实现方式。通过 `Channel` 实现 `生产-消费` 机制可以减少项目间的耦合，提高应用吞吐量。

`Furion` 框架提供了 `ChannelContext<TMessage, THandler>` 密封类，提供 `UnBoundedChannel` 和 `BoundedChannel` 两种管道通信模式。

- `UnBoundedChannel`：具有无限容量的 `Channel`, 生产者可以全速进行生产数据，但如果消费者的消费速度低于生产者，`Channel` 的资源使用会无限增加，会有服务器资源耗尽的可能。
- `BoundedChannel`：具有有限容量的 `Channel`，`Furion` 框架默认为 `1000`，到达上限后，生产者进入等待写入直到有空闲，好处是可以控制生产的速度，控制系统资源的使用。**（推荐）**

### 33.4.2 常规使用

#### 创建 `ChannelHandler<TMessage>` 管道处理程序

```cs {1,10,17}
using Furion.IPCChannel;
using System;
using System.Threading.Tasks;

namespace Furion.Core
{
    /// <summary>
    /// 创建管道处理程序（处理 String 类型消息）
    /// </summary>
    public class MyChannelHandler : ChannelHandler<string>
    {
        /// <summary>
        /// 接受到管道消息后处理程序
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        public override Task InvokeAsync(string message)
        {
            Console.WriteLine(message);

            return Task.CompletedTask;
        }
    }
}
```

:::note

` ChannelHandler<TMessage>` 泛型类型决定了你要接受那种类型的消息，不同类型消息将会自动过滤筛选。

:::

#### 使用 `ChannelContext<TMessage, THandler>` 发送消息

```cs {5-6}
public async Task SendAsync()
{
    for (int i = 0; i < 100; i++)
    {
        // 使用有限容量生产数据
        await ChannelContext<string, MyChannelHandler>.BoundedChannel.Writer.WriteAsync($"Loop {i} times.");
    }
}
```

以上代码也可以通过 `ChannelContext<string, MyChannelHandler>.BoundedChannel.Writer.TryWrite()` 同步写入。

### 33.4.3 实现多订阅

默认情况下，`Furion` 初始化了一个长时间的 `Task` 任务进行数据检查及订阅，如需实现多订阅模式，可创建新的订阅任务即可：

```cs
var reader = ChannelContext<string, MyChannelHandler>.BoundedChannel.Reader;

// 创建长时间线程管道读取器
_ = Task.Factory.StartNew(async () =>
  {
      while (await reader.WaitToReadAsync())
      {
          if (!reader.TryRead(out var message)) continue;
          // 默认重试 3 次（每次间隔 1s）
          await Retry.Invoke(async () => await Activator.CreateInstance<MyChannelHandler>().InvokeAsync(message), 3, 1000, finalThrow: false);
      }
  }, TaskCreationOptions.LongRunning);
```

### 33.4.4 更多 `Channel` 知识

可查阅 [Dotnet Core 下的 Channel, 你用了吗？](https://www.cnblogs.com/tiger-wang/p/14068973.html) 博客教程（😃 写的不错）

### 33.4.5 `CallContext` 方式

在 `Furion v2.18+` 版本提供了 `CallContext` 静态类，内部使用 `AsyncLocal<T>` 实现，也可以实现线程间通信，如：

```cs
CallContext.SetLocalValue("name", "Furion");
CallContext.GetLocalValue("name");

CallContext<int>.SetLocalValue("count", 1);
CallContext<int>.GetLocalValue("count");
```

## 34.5 进程外通信（共享内存）

`Furion` 目前暂未提供的进程外通信功能，将在后续版本实现（主要是模块设计还未想好，技术已实现）。

主要是通过 `MemoryMapperFile` 实现共享内存达到进程外通信功能，[了解更多 MemoryMapperFile](https://docs.microsoft.com/zh-cn/dotnet/api/system.io.memorymappedfiles.memorymappedfile?view=net-5.0)

## 33.6 反馈与建议

:::note 与我们交流

给 Furion 提 [Issue](https://gitee.com/dotnetchina/Furion/issues/new?issue)。

:::
